/*
 *     This file is part of Mobile GPS Logger.
 *
 *     Mobile GPS Logger is free software: you can redistribute it and/or
 *     modify it under the terms of the GNU General Public License as
 *     published by the Free Software Foundation, either version 3 of the
 *     License, or (at your option) any later version.
 *
 *     Mobile GPS Logger is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License v3 for more details.
 *
 *     You should have received a copy of the GNU General Public License v3
 *     along with Mobile GPS Logger. If not, see <http://www.gnu.org/licenses/>.
 */

package cx.ath.skyflyer.gpslog;

import cx.ath.skyflyer.contactless.*;
import java.io.IOException;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.List;
import javax.microedition.midlet.MIDlet;
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;


/**
 * Contains the entry point of this application.
 * @author Ahti Legonkov
 */
public final class GpsLogMidlet
        extends MIDlet
        implements CommandListener, RecordListener
{

    /**
     * Constructs the MIDlet.
     */
    public GpsLogMidlet() {
    }

    /**
     * Creates the view and action buttons.
     */
    public void startApp() {

        //writeFakeDefault();

        m_Menu = new List("Gps Logger", List.IMPLICIT);
        
        // Check if default is available in record store, add this menu
        // item only if record store contains a record for default URL.
        try {
            String name = Configuration.getDefaultGpsDeviceName();
            this.idUseDefault = m_Menu.append("Use "+name, bluetoothIcon());
        }
        catch (Exception ex) {
            // No "Use default" because there is no default URL saved.
            //m_Menu.append(ex.toString(), null);
        }

        this.idDiscover = m_Menu.append("Discover devices", null);
        this.idChart = m_Menu.append("View Chart", chartIcon());
        this.idSettings = m_Menu.append("Settings", null);

        m_Menu.addCommand(CMD_EXIT);
        m_Menu.setCommandListener(this);

        getDisplay().setCurrent(m_Menu);

        registerApplication();
    }

    private static Image loadImage(String name) {
        try {
            return Image.createImage(name);
        }
        catch (Exception ex) {
            return null;
        }
    }

    private static Image chartIcon() {
        return loadImage("/images/chart_icon.png");
    }

    private static Image bluetoothIcon() {
        return loadImage("/images/bt_logo_16.png");
    }

    /**
     * Writes a default URL to the record store. Used only for testing. For some
     * reason I am not able to make the changes to the Nokia emulator persist.
     */
    private void writeFakeDefault() {
        try {
            Configuration.setDefaultGpsDeviceUrl("btspp://123456789012:2");
            Configuration.setDefaultGpsDeviceName("Fake GPS");
        }
        catch (ConfigurationException ex) {
            System.out.println("Failed to save: "+ex);
        }
    }

    private void registerApplication() {
        NdefClient.register(this);

        /*try {
            DiscoveryManager dm = DiscoveryManager.getInstance();
            NDEFRecordListener recordListener = this;
            dm.addNDEFRecordListener(recordListener, new NDEFRecordType(
                    NDEFRecordType.EXTERNAL_RTD, "urn:nfc:ext:skyflyer.ath.cx:gpslog"));
        } catch (ContactlessException e) {
            // Catch ContactlessException
        }

        try {
            // Make list of connections that are already registered
            String[] regConns = PushRegistry.listConnections(false);

            // Boolean to tell if wanted TargetType is already registered
            boolean registered = false;

            // Go trough list to see if wanted TargetType is already registered
            for (int i = 0; i < regConns.length; i++) {
                if (regConns[i].equals("ndef:external_rtd?name=urn:nfc:ext:skyflyer.ath.cx:gpslog")) {
                    registered = true;
                    break;
                }
            }
            // If TargetType is not registered - register it
            if (!registered) {
                // Register this MIDlet to be launched when any tag with right
                // TargetType is touched
                //PushRegistry
                //        .registerConnection(
                //                "ndef:external_rtd?name=urn:nfc:ext:skyflyer.ath.cx:gpslog",
                //                "cx.ath.skyflyer.gpslog.GpsLogMidlet", "*");
                //pushTextField.setString("Succeeded");
                //f.append("Not registered");
            } else {
                // If target was alredy regisered write that on screen
                //f.append("Already registered");
            }
        } catch (Exception e) {
            // In case of exception write message on screen
            //f.append("Exception: " + e.getMessage());
        }*/
    }

    /**
     * Destroys the application.
     */
    protected void destroyApp(boolean unconditional) { }

    /**
     * Redefinition is required by MIDlet class.
     */
    protected void pauseApp() { }

    /**
     * Responds to commands issued on "client" form.
     *
     * @param c command object source of action
     * @param d screen object containing the item action was performed on
     */
    public void commandAction(Command c, Displayable d) {
        if (c == CMD_EXIT) {
            onExit();
        }
        else if (c == List.SELECT_COMMAND) {
            onSelect((List)d);
        }
    }

    //
    // Implementation of NDEFRecordListener
    //

    /**
     * If the midlet was started as a result of touching an RFID tag, this
     * method is called.
     * @param ndefMessage a message that was read from the RFID tag.
     */
    /*public void recordDetected(NDEFMessage ndefMessage) {

        int recordCount = ndefMessage.getRecords().length;

        // Check if the message carries a payload. If it does, the payload is
        // assumed to be a URL needed to connect to a bluetooth device and the
        // device and service discovery pages are skipped.
        for (int i = 0; i < recordCount; ++i) {
            NDEFRecord rec = ndefMessage.getRecord(i);
            byte[] payload = rec.getPayload();
            if (payload == null)
                continue;

            String url = new String(payload);
            Logger logger = new Logger(this, menu, url);
            break;
        }
    }*/

    public void recordDetected(final String url) {

        //assert(NdefClient.useNfc());
        if (!NdefClient.useNfc())
            return;

        Alert a = new Alert("");
        if (url.length() > 0) {
            final Displayable prev = getDisplay().getCurrent();
            a.setString("Do you want to use this URL: " + url);
            final Command cmdYes = new Command("Yes", Command.OK, 1);
            final Command cmdNo = new Command("No", Command.CANCEL, 1);
            a.addCommand(cmdYes);
            a.addCommand(cmdNo);
            a.setTimeout(Alert.FOREVER);
            a.setCommandListener(new CommandListener() {
                public void commandAction(Command c, Displayable d) {
                    if (c == cmdYes) {
                        startLogger(url);
                    }
                    else {
                        getDisplay().setCurrent(prev);
                    }
                }
            });
            getDisplay().setCurrent(a);
        }
    }

    private void startLogger(String url) {
        Logger logger = new Logger(this, m_Menu, url);
    }

    private DeviceList m_Devices;

    private void onSelect(List list) {
        final int index = list.getSelectedIndex();

        if (index == this.idDiscover)
            onDiscoverDevicesSelected();

        else if (index == this.idChart)
            onViewChart();

        else if (index == this.idUseDefault)
            onUseDefaultSelected();

        else if (index == this.idSettings)
            onSettings();
    }

    private void onDiscoverDevicesSelected() {
        // start the discovery process
        m_Devices = new DeviceList(this);
        m_Devices.discover();
    }

    private void onViewChart() {
        try {
            final FileList fl = new FileList(Configuration.getLogPath(), "Select file");
            final Display display = getDisplay();
            final MIDlet midlet = this;

            fl.setCommandListener(new CommandListener() {

                public void commandAction(Command c, Displayable d) {

                    try {
                        if (c == FileList.CMD_FILE_SELECTED) {
                            FileList lst = (FileList)d;
                            final String file = lst.getSelectedFilePath();
                            System.out.println("file="+file);
                            final SpeedChart chart = new SpeedChart(midlet, file);
                            display.setCurrent(chart);
                        }
                        else if (c == FileList.CMD_BACK) {
                            display.setCurrent(m_Menu);
                        }
                    }
                    catch (IOException ex) {
                        // TODO: report error.
                        Alert a = new Alert("I/O error", ex.getMessage(),
                                null, AlertType.ERROR);
                        display.setCurrent(a, m_Menu);
                    }
                }
            });
            display.setCurrent(fl);
        }
        catch (SecurityException ex) {
            Alert a = new Alert(
                    "Security alert",
                    "You must allow the operation if you want to select a file.",
                    null,
                    AlertType.INFO);
            getDisplay().setCurrent(a, m_Menu);
        }
        catch (Exception ex) {
            Alert a = new Alert("Error", ex.toString(), null, AlertType.ERROR);
            getDisplay().setCurrent(a, m_Menu);
        }
    }

    private void onUseDefaultSelected() {

        // load the default URL and use it.

        try {
            String url = Configuration.getDefaultGpsDeviceUrl();
            startLogger(url);
            return;
        }
        catch (ConfigurationException ex) {
            Alert a = new Alert("Use default");
            a.setTimeout(5000);
            a.setString("Save default first");
            a.setType(AlertType.INFO);
            Display d = getDisplay();
            d.setCurrent(a, d.getCurrent());
        }
    }

    private void onSettings() {
        SettingsForm f = new SettingsForm(this, getDisplay().getCurrent());
        getDisplay().setCurrent(f);
    }

    private void onExit() {
        exit();
    }

    public void exit() {
        destroyApp(true);
        notifyDestroyed();
    }

    /**
     * Returns the displayable object of this screen -
     * it is required for Alert construction for the error
     * cases.
     */
    Displayable getDisplayable() {
        return m_Menu;
    }

    /**
     * Returns a display for this midlet.
     * @return
     */
    public Display getDisplay() {
        return Display.getDisplay(this);
    }

    /** The messages are shown in this demo this amount of time. */
    static final int ALERT_TIMEOUT = 2000;

    /** A Command to exit the application. */
    private final Command CMD_EXIT = new Command("Exit", Command.EXIT, 2);

    /** A menu list instance */
    private List m_Menu;

    private int idDiscover = -1;
    private int idChart = -1;
    private int idUseDefault = -1;
    private int idSettings = -1;

} // end of class 'GpsLogMidlet' definition
